/* 
 * Quechua - the lightweight data mining framework
 *
 * Copyright (C) 2012 Marek Denis <quechua@octogan.net>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "attributes.h"

const char* Attributes::default_desc = "ATTRNOTFOUND";

Attribute::Attribute(const attribute_cfg& cfg) {
    type = cfg.type;
    desc = cfg.desc;
    range= cfg.range;
    index= cfg.index;
};


const char* Attribute::get_desc() {
    return desc;
}

index_t& Attribute::get_index() {
    return index;
};

u_int16_t Attribute::get_range() {
    return range;
}

Attributes::Attributes() : index(1) {};

Attributes::~Attributes() {
    attributes.clear();
};

index_t Attributes::allocate_index() {
    return index++;
}

inline void Attributes::extract_index(const index_t& range) {
    index = index+range;
}

ATTR_TYPE Attribute::get_type() {
    return type;
}

void Attributes::set_attribute(ATTR_TYPE type, const char* desc, const u_int16_t range) {
    if(type==RANGE_ATTRIBUTE) 
        set_range_attribute(desc,range);
    else if(type==SINGLE_ATTRIBUTE)
        set_single_attribute(desc);
    else
        LOG(WARN) << "Attributes::set_attribute() : bad attribute type, skipping";
}

index_t Attributes::get_attribute_index(const char* desc, const u_int16_t value) {
    attributes_list::iterator it,end;
    it = attributes.begin();
    end= attributes.end();

    Attribute *attr;
    for(;it!=end;++it) {
        attr = *it;
        if(!string_t(attr->get_desc()).compare(desc)) { // equal
            if(attr->get_type() == SINGLE_ATTRIBUTE)
                return attr->get_index();
            else if(attr->get_type() == RANGE_ATTRIBUTE && value <= attr->get_range()) {
                return attr->get_index() + value;
            }
        } 
    };
    return 0;
}

attribute_val* Attributes::get_attribute_value(const index_t& idx, attribute_val* value) {
    attributes_list::iterator it,end;
    it = attributes.begin();
    end= attributes.end();
    Attribute *attr;
    for(;it!=end;++it) {
        attr = *it;
        if(attr->get_type() == SINGLE_ATTRIBUTE && attr->get_index() == idx) {
            value->desc = attr->get_desc();
            value->value= attr->get_index();
            return value;
        } else if(attr->get_type() == RANGE_ATTRIBUTE && idx >= attr->get_index() && idx <= attr->get_index() + attr->get_range()) {

            value->desc = attr->get_desc();
            value->value= idx - attr->get_index() - attr->get_range() -1;
            return value;
        }
    }
    return NULL;
}

void Attributes::set_range_attribute(const char* desc, const u_int16_t range) {
    attribute_cfg cfg;

    cfg.type = RANGE_ATTRIBUTE;
    cfg.desc = desc;
    cfg.range = range;
    cfg.index = allocate_index();

    extract_index(range);
    Attribute *attribute = new Attribute(cfg);

    try {
        attributes.push_back(attribute);
    } catch(alloc_ex& ex) {
        LOG(ERROR) << "Cannot allocate memory for next attribute,skipping";
        delete attribute;
        extract_index((-1)*range);
    }
};

void Attributes::set_single_attribute(const char* desc) {
    attribute_cfg cfg;
    cfg.type = SINGLE_ATTRIBUTE;
    cfg.desc = desc;
    cfg.range = 1;
    cfg.index = allocate_index();
    Attribute *attribute = new Attribute(cfg);

    try {
        attributes.push_back(attribute);
    } catch(alloc_ex& ex) {
        LOG(ERROR) << "Cannot allocate memory for next attribute,skipping";
        delete attribute;
        extract_index(-1);
    }
};

/**
 * TODO(marek)
 * Linear search should be fine right now as very few attributes will be there.
 * Think about binary search in the future.
 **/ 
const char* Attributes::get_desc_by_val(const index_t& val) {
    attributes_list::iterator it,end;
    it = attributes.begin();
    end= attributes.end();
    for(;it!=end;++it) {
        if(check_attribute(*it,val))
            return (*it)->get_desc();
    }

    return default_desc;
}

/**
 * Checks whether attribute is the one we are looking for.
 **/
bool Attributes::check_attribute(Attribute* attr,const index_t& val) {
    if(attr->get_type() == SINGLE_ATTRIBUTE && attr->get_index() == val)
        return true;
    
    if(val >= attr->get_index() && val - attr->get_index() <= attr->get_range())
        return true;

    return false;
};
